/**
 * 杰理AC79xx WiFi AP配网服务器模块
 *
 * 依赖（请在wifi task中启动DHCP与DNS）:
 *   dhcps_offer_dns();
 *   u32 dns_server = 0xC0A80801; // 192.168.8.1
 *   dns_setserver(0, &dns_server);
 *   dhcps_init(0);
 */
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "sock_api/sock_api.h"
#include "os/os_api.h"
#include "app_config.h"
#include "system/includes.h"
#include "wifi/wifi_connect.h"
#include "lwip.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "dhcp_srv/dhcp_srv.h"

#include "wifi_prov_server_v1.h"

// 网页定义
#include "wifi_prov_web_page.h"

// ---------- 日志 ----------
#define PLOG(fmt, ...)                               \
    do                                               \
    {                                                \
        printf("[prov_server] " fmt, ##__VA_ARGS__); \
    } while (0)

#define DEFAULT_HTTP_PORT 80
#define DEFAULT_CAPTIVE_IP 0xC0A80801 // 192.168.8.1

typedef enum
{
    WIFI_PROV_SERVER_STOPPED = 0,
    WIFI_PROV_SERVER_STARTING,
    WIFI_PROV_SERVER_RUNNING,
    WIFI_PROV_SERVER_STOPPING,
    WIFI_PROV_SERVER_ERROR
} wifi_prov_server_state_t;

typedef struct
{
    wifi_prov_server_state_t state;
    // wifi_prov_server_config_t cfg;
    // wifi_prov_server_cb_t cb;

    wifi_prov_server_cfg_t cfg;

    int server_pid;
    int accept_pid;
    void *server_socket;

    OS_MUTEX mtx;
    u8 initialized;
} prov_ctx_t;

static prov_ctx_t g_ctx;

static wifi_prov_server_state_t wifi_prov_server_get_state(void)
{
    wifi_prov_server_state_t st;
    os_mutex_pend(&g_ctx.mtx, 0);
    st = g_ctx.state;
    os_mutex_post(&g_ctx.mtx);
    return st;
}

// =============== 工具函数 ===============
static void url_decode(char *s)
{
    char *p = s, *o = s;
    while (*p)
    {
        if (*p == '+')
        {
            *o++ = ' ';
            p++;
        }
        else if (*p == '%' && p[1] && p[2])
        {
            int hi = (p[1] >= 'A') ? ((p[1] & ~0x20) - 'A' + 10) : (p[1] - '0');
            int lo = (p[2] >= 'A') ? ((p[2] & ~0x20) - 'A' + 10) : (p[2] - '0');
            if ((unsigned)hi < 16 && (unsigned)lo < 16)
            {
                *o++ = (char)((hi << 4) | lo);
                p += 3;
            }
            else
            {
                *o++ = *p++;
            }
        }
        else
        {
            *o++ = *p++;
        }
    }
    *o = '\0';
}

static void query_get(const char *query, const char *key, char *out, int out_len)
{
    out[0] = 0;
    if (!query || !key)
        return;
    const char *p = query;
    int klen = strlen(key);
    while (p && *p)
    {
        const char *amp = strchr(p, '&');
        int seglen = amp ? (int)(amp - p) : (int)strlen(p);
        if (seglen > klen + 1 && !memcmp(p, key, klen) && p[klen] == '=')
        {
            int vlen = seglen - (klen + 1);
            if (vlen >= out_len)
                vlen = out_len - 1;
            memcpy(out, p + klen + 1, vlen);
            out[vlen] = 0;
            url_decode(out);
            return;
        }
        p = amp ? (amp + 1) : NULL;
    }
}

static int json_escape(char *dst, int dst_len, const char *src)
{
    int n = 0;
    for (; src && *src && n < dst_len - 1; src++)
    {
        unsigned char c = (unsigned char)*src;
        if (c == '\"' || c == '\\')
        {
            if (n >= dst_len - 2)
                break;
            dst[n++] = '\\';
            dst[n++] = c;
        }
        else if (c < 0x20)
        {
            // skip
        }
        else
        {
            dst[n++] = c;
        }
    }
    dst[n] = 0;
    return n;
}

// =============== HTTP响应封装 ===============
static void send_http_text(int cfd, const char *ctype, const char *fmt, ...)
{
    char body[512];
    va_list ap;
    va_start(ap, fmt);
    int n = vsnprintf(body, sizeof(body), fmt, ap);
    va_end(ap);
    if (n < 0)
        n = 0;
    if (n > (int)sizeof(body))
        n = sizeof(body);

    char hdr[256];
    int hn = snprintf(hdr, sizeof(hdr),
                      "HTTP/1.1 200 OK\r\nContent-Type: %s; charset=utf-8\r\nConnection: close\r\nContent-Length: %d\r\n\r\n",
                      ctype ? ctype : "text/plain", n);
    sock_send(cfd, hdr, hn, 0);
    sock_send(cfd, body, n, 0);
}

static void send_http_html(int cfd, const char *html, int len)
{
    if (len < 0)
        len = strlen(html);
    char hdr[256];
    int hn = snprintf(hdr, sizeof(hdr),
                      "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: %d\r\n\r\n",
                      len);
    sock_send(cfd, hdr, hn, 0);
    sock_send(cfd, html, len, 0);
}

static void send_redirect(int cfd, const char *location)
{
    char hdr[256];
    int hn = snprintf(hdr, sizeof(hdr),
                      "HTTP/1.1 302 Found\r\nLocation: %s\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
                      location);
    sock_send(cfd, hdr, hn, 0);
}

static char *get_header_value(const char *request, const char *header_name)
{
    char *line_start = strstr(request, header_name);
    if (!line_start)
        return NULL;
    line_start += strlen(header_name);
    while (*line_start == ' ' || *line_start == ':')
        line_start++;
    char *line_end = strchr(line_start, '\r');
    if (!line_end)
        line_end = strchr(line_start, '\n');
    if (!line_end)
        return NULL;
    static char value[256];
    int len = line_end - line_start;
    if (len >= sizeof(value))
        len = sizeof(value) - 1;
    if (len <= 0)
        return NULL;
    strncpy(value, line_start, len);
    value[len] = '\0';
    while (len > 0 && (value[len - 1] == ' ' || value[len - 1] == '\t'))
        value[--len] = '\0';
    return value;
}

// =============== Captive Portal ===============
static int is_captive_portal_request(const char *uri, const char *host)
{
    if (!strcmp(uri, "/") ||
        !strncmp(uri, "/index.html", 11) ||
        !strncmp(uri, "/scan", 5) ||
        !strncmp(uri, "/status", 7) ||
        !strncmp(uri, "/wifi_cfg", 9) ||
        !strncmp(uri, "/favicon.ico", 12))
    {
        return 0;
    }
    if (!strcmp(uri, "/generate_204") ||
        !strcmp(uri, "/hotspot-detect.html") ||
        !strcmp(uri, "/ncsi.txt") ||
        !strcmp(uri, "/connectivity-check.html"))
    {
        return 1;
    }
    if (host && (strstr(host, "captive.apple.com") ||
                 strstr(host, "connectivitycheck.gstatic.com") ||
                 strstr(host, "msftconnecttest.com")))
    {
        return 1;
    }
    return 0;
}

// =============== 配网业务粘合点 ===============
typedef struct
{
    char ssid[64];
    char pwd[64];
    char save;
} connect_args_t;

static void wifi_connect_task(void *priv)
{
    connect_args_t *a = (connect_args_t *)priv;
    os_time_dly(100);
    printf("wifi_connect_task: ssid='%s' save=%d\n", a->ssid, a->save);
    // 给用户保存：当真正获取IP时才保存覆盖默认配置，避免配网不成功时连接出错
    // if (a->save == '1')
    // {
    //     printf("wifi_store_mode_info save\n");
    //     wifi_store_mode_info(STA_MODE, a->ssid, a->pwd);
    // }
    free(a);
}

static int start_wifi_connect_async(const char *ssid, const char *pwd, char save)
{
    connect_args_t *a = (connect_args_t *)malloc(sizeof(connect_args_t));
    if (!a)
        return -1;
    memset(a, 0, sizeof(*a));
    strncpy(a->ssid, ssid, sizeof(a->ssid) - 1);
    strncpy(a->pwd, pwd, sizeof(a->pwd) - 1);
    a->save = save;
    int pid = -1;
    if (thread_fork("wifi_connect_task", 23, 1024, 0, &pid, wifi_connect_task, a) != OS_NO_ERR)
    {
        free(a);
        return -2;
    }
    return 0;
}

// 将凭据抛给上层回调
static void invoke_on_credentials(const char *ssid, const char *pwd, int save)
{
    if (g_ctx.cfg.cb)
    {
        g_ctx.cfg.cb(ssid ? ssid : "", pwd ? pwd : "", save);
    }
}

// =============== HTTP请求处理 ===============
static void handle_client(void *cfd_hdl)
{
    char req[1024];
    int r = sock_recv(cfd_hdl, req, sizeof(req) - 1, 0);
    if (r <= 0)
        return;
    req[r] = 0;

    char *p = req;
    char *sp1 = strchr(p, ' ');
    if (!sp1)
        return;
    *sp1 = 0;
    char *method = p;
    char *sp2 = strchr(sp1 + 1, ' ');
    if (!sp2)
        return;
    *sp2 = 0;
    char *uri = sp1 + 1;
    char *host = get_header_value(req, "Host");

    if (strcmp(method, "GET"))
    {
        const char *resp = "HTTP/1.1 405 Method Not Allowed\r\nConnection: close\r\n\r\n";
        sock_send(cfd_hdl, resp, strlen(resp), 0);
        return;
    }

    if (is_captive_portal_request(uri, host))
    {
        char location[64];
        snprintf(location, sizeof(location), "http://%d.%d.%d.%d/",
                 (g_ctx.cfg.captive_portal_ip >> 24) & 0xFF,
                 (g_ctx.cfg.captive_portal_ip >> 16) & 0xFF,
                 (g_ctx.cfg.captive_portal_ip >> 8) & 0xFF,
                 g_ctx.cfg.captive_portal_ip & 0xFF);
        send_redirect((int)cfd_hdl, location);
        return;
    }

    if (!strcmp(uri, "/") || !strncmp(uri, "/index.html", 11))
    {
        send_http_html((int)cfd_hdl, index_body, -1);
        return;
    }

    if (!strncmp(uri, "/scan", 5))
    {
        u32 num = 0;
        struct wifi_scan_ssid_info *arr = NULL;
        char buf[1024];
        int pos = 0;

        wifi_clear_scan_result();
        wifi_scan_req();
        os_time_dly(100);

        arr = wifi_get_scan_result(&num);
        printf("[prov_server] wifi_scan result: %d\n", (int)num);
        pos += snprintf(buf + pos, sizeof(buf) - pos, "[");
        for (u32 i = 0; i < num && pos < (int)sizeof(buf) - 64; i++)
        {
            char esc[96];
            esc[0] = 0;
            if (arr[i].ssid)
                json_escape(esc, sizeof(esc), arr[i].ssid);
            pos += snprintf(buf + pos, sizeof(buf) - pos,
                            "%s{\"ssid\":\"%s\",\"rssi\":%d}",
                            (i ? "," : ""), esc, arr[i].rssi);
        }
        pos += snprintf(buf + pos, sizeof(buf) - pos, "]");
        if (arr)
            free(arr);
        wifi_clear_scan_result();

        send_http_text((int)cfd_hdl, "application/json", "%s", buf);
        // printf("[prov_server] scan json: %s\n", buf);
        return;
    }

    if (!strncmp(uri, "/status", 7))
    {
        enum wifi_sta_connect_state st = wifi_get_sta_connect_state();
        char st_str[24] = "UNKNOWN";
        int rssi = (int)wifi_get_rssi();
        switch (st)
        {
            case 0:
                strcpy(st_str, "IDLE");
                break;
            case 1:
                strcpy(st_str, "SCANNING");
                break;
            case 2:
                strcpy(st_str, "CONNECTING");
                break;
            case 3:
                strcpy(st_str, "CONNECTED");
                break;
            case 4:
                strcpy(st_str, "DISCONNECT");
                break;
            default:
                strcpy(st_str, "UNKNOWN");
                break;
        }
        send_http_text((int)cfd_hdl, "application/json",
                       "{\"state\":%d,\"state_str\":\"%s\",\"rssi\":%d,\"ip\":\"\"}", (int)st, st_str, rssi);
        return;
    }

    if (!strncmp(uri, "/wifi_cfg", 9))
    {
        const char *q = strchr(uri, '?');
        char ssid[64] = {0}, pwd[64] = {0}, save[8] = {0};
        if (q)
        {
            q++;
            query_get(q, "ssid", ssid, sizeof(ssid));
            query_get(q, "pwd", pwd, sizeof(pwd));
            query_get(q, "save", save, sizeof(save));
        }
        if (!ssid[0])
        {
            send_http_text((int)cfd_hdl, "application/json", "{\"ok\":0,\"msg\":\"SSID 不能为空\"}");
            return;
        }
        send_http_text((int)cfd_hdl, "application/json",
                       "{\"ok\":1,\"msg\":\"正在连接 WiFi...\",\"ssid\":\"%s\"}", ssid);

        start_wifi_connect_async(ssid, pwd, save[0] ? save[0] : '0');

        // 同时把凭据抛给上层回调（让上层决定是否接管连接/保存等）
        invoke_on_credentials(ssid, pwd, (save[0] == '1') ? 1 : 0);
        return;
    }

    // 其他请求重定向到 /
    {
        char location[64];
        snprintf(location, sizeof(location), "http://%d.%d.%d.%d/",
                 (g_ctx.cfg.captive_portal_ip >> 24) & 0xFF,
                 (g_ctx.cfg.captive_portal_ip >> 16) & 0xFF,
                 (g_ctx.cfg.captive_portal_ip >> 8) & 0xFF,
                 g_ctx.cfg.captive_portal_ip & 0xFF);
        send_redirect((int)cfd_hdl, location);
    }
}

// =============== 服务器任务 ===============
static void http_accept_loop(void *priv)
{
    void *srv = g_ctx.server_socket;
    void *cli = NULL;
    struct sockaddr_in raddr;
    socklen_t rlen = sizeof(raddr);

    while (1)
    {
        os_mutex_pend(&g_ctx.mtx, 0);
        int running = (g_ctx.state == WIFI_PROV_SERVER_RUNNING);
        os_mutex_post(&g_ctx.mtx);
        if (!running)
            break;

        cli = sock_accept(srv, (struct sockaddr *)&raddr, &rlen, NULL, NULL);
        if (!cli)
        {
            os_time_dly(10);
            continue;
        }
        handle_client(cli);
        sock_unreg(cli);
    }
}

static void http_server_task(void *priv)
{
    void *srv = NULL;
    struct sockaddr_in addr;
    u32 opt = 1;

    PLOG("http_server_task: start\n");

    srv = sock_reg(AF_INET, SOCK_STREAM, 0, NULL, NULL);
    if (!srv)
    {
        os_mutex_pend(&g_ctx.mtx, 0);
        g_ctx.state = WIFI_PROV_SERVER_ERROR;
        os_mutex_post(&g_ctx.mtx);
        return;
    }
    g_ctx.server_socket = srv;

    if (sock_setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
    {
        sock_unreg(srv);
        g_ctx.server_socket = NULL;
        os_mutex_pend(&g_ctx.mtx, 0);
        g_ctx.state = WIFI_PROV_SERVER_ERROR;
        os_mutex_post(&g_ctx.mtx);
        return;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(g_ctx.cfg.port);

    if (sock_bind(srv, (struct sockaddr *)&addr, sizeof(addr)))
    {
        sock_unreg(srv);
        g_ctx.server_socket = NULL;
        os_mutex_pend(&g_ctx.mtx, 0);
        g_ctx.state = WIFI_PROV_SERVER_ERROR;
        os_mutex_post(&g_ctx.mtx);
        return;
    }
    if (sock_listen(srv, 1) != 0)
    {
        sock_unreg(srv);
        g_ctx.server_socket = NULL;
        os_mutex_pend(&g_ctx.mtx, 0);
        g_ctx.state = WIFI_PROV_SERVER_ERROR;
        os_mutex_post(&g_ctx.mtx);
        return;
    }

    os_mutex_pend(&g_ctx.mtx, 0);
    g_ctx.state = WIFI_PROV_SERVER_RUNNING;
    os_mutex_post(&g_ctx.mtx);

    // accept loop
    thread_fork("prov_accept", 24, 2048, 0, &g_ctx.accept_pid, http_accept_loop, NULL);

    // 等待停止信号
    while (1)
    {
        os_mutex_pend(&g_ctx.mtx, 0);
        int stopping = (g_ctx.state == WIFI_PROV_SERVER_STOPPING);
        int running = (g_ctx.state == WIFI_PROV_SERVER_RUNNING);
        os_mutex_post(&g_ctx.mtx);
        if (!running && !stopping)
            break;
        os_time_dly(100);
    }

    // 关闭server
    if (g_ctx.server_socket)
    {
        sock_unreg(g_ctx.server_socket);
        g_ctx.server_socket = NULL;
    }

    os_mutex_pend(&g_ctx.mtx, 0);
    g_ctx.state = WIFI_PROV_SERVER_STOPPED;
    os_mutex_post(&g_ctx.mtx);
}

static void prov_http_server_stop_task(void *priv)
{
    if (!g_ctx.initialized)
        return;

    os_time_dly(100);
    http_server_stop_async();

    // 简单等待退出
    int tries = 5;
    while (tries--)
    {
        if (wifi_prov_server_get_state() == WIFI_PROV_SERVER_STOPPED)
            break;
        os_time_dly(100);
    }

    os_mutex_del(&g_ctx.mtx, OS_DEL_ALWAYS);
    // memset(&g_ctx, 0, sizeof(g_ctx));
    printf("[prov_server] prov_http_server_stop_task: stopped\n");
}

static int http_server_start_async(const wifi_prov_server_cfg_t *cfg)
{
    if (g_ctx.initialized)
        return 0;

    memset(&g_ctx, 0, sizeof(g_ctx));
    os_mutex_create(&g_ctx.mtx);

    g_ctx.cfg.port = DEFAULT_HTTP_PORT;
    g_ctx.cfg.captive_portal_ip = DEFAULT_CAPTIVE_IP;

    if (cfg)
        g_ctx.cfg = *cfg;

    os_mutex_pend(&g_ctx.mtx, 0);
    g_ctx.state = WIFI_PROV_SERVER_STARTING;
    g_ctx.initialized = 1;
    os_mutex_post(&g_ctx.mtx);

    int pid = -1;
    if (thread_fork("prov_http_server_task", 23, 1024, 0, &pid, http_server_task, NULL) != OS_NO_ERR)
    {
        os_mutex_pend(&g_ctx.mtx, 0);
        g_ctx.state = WIFI_PROV_SERVER_ERROR;
        os_mutex_post(&g_ctx.mtx);
        return -2;
    }
    return 0;
}

static int http_server_stop_async(void)
{
    if (!g_ctx.initialized)
        return -1;

    os_mutex_pend(&g_ctx.mtx, 0);
    if (g_ctx.state != WIFI_PROV_SERVER_RUNNING)
    {
        os_mutex_post(&g_ctx.mtx);
        return 0;
    }

    if (g_ctx.state == WIFI_PROV_SERVER_RUNNING)
    {
        g_ctx.state = WIFI_PROV_SERVER_STOPPING;
    }
    os_mutex_post(&g_ctx.mtx);

    printf("prov_stop_trampoline: stopping\n");
    return 0;
}

static int http_server_init(const wifi_prov_server_cfg_t *cfg)
{
    return http_server_start_async(cfg);
}

static void http_server_deinit(void)
{
    int pid = -1;
    thread_fork("prov_http_server_stop_task", 23, 1024, 0, &pid, prov_http_server_stop_task, NULL);
}

// =============== 公共API ===============
int wifi_prov_server_start(const wifi_prov_server_cfg_t *cfg)
{
    return http_server_init(cfg);
}

int wifi_prov_server_stop(void)
{
    http_server_deinit();
    return 0;
}
